home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
CICA 1995 August
/
CICA - The Ultimate Collection of Shareware for Windows (Disc 2) (August 1995).iso
/
disc2
/
nt
/
source.exe
/
POSIX
/
ELVIS
/
TIO.C
< prev
next >
Wrap
C/C++ Source or Header
|
1993-07-06
|
17KB
|
864 lines
/* tio.c */
/* Author:
* Steve Kirkendall
* 14407 SW Teal Blvd. #C
* Beaverton, OR 97005
* kirkenda@cs.pdx.edu
*/
/* This file contains terminal I/O functions */
#include "config.h"
#if BSD || COHERENT
# include <setjmp.h>
#endif
#include <signal.h>
#include "vi.h"
//#include "curses.h"
/* This function reads in a line from the terminal. */
int vgets(prompt, buf, bsize)
char prompt; /* the prompt character, or '\0' for none */
char *buf; /* buffer into which the string is read */
int bsize; /* size of the buffer */
{
int len; /* how much we've read so far */
int ch; /* a character from the user */
int quoted; /* is the next char quoted? */
int tab; /* column position of cursor */
char widths[132]; /* widths of characters */
#ifndef NO_DIGRAPH
int erased; /* 0, or first char of a digraph */
#endif
/* show the prompt */
move(LINES - 1, 0);
tab = 0;
if (prompt)
{
addch(prompt);
tab = 1;
}
clrtoeol();
refresh();
/* read in the line */
#ifndef NO_DIGRAPH
erased =
#endif
quoted = len = 0;
for (;;)
{
ch = getkey(quoted ? 0 : WHEN_EX);
/* some special conversions */
if (ch == ctrl('D') && len == 0)
ch = ctrl('[');
#ifndef NO_DIGRAPH
if (*o_digraph && erased != 0 && ch != '\b')
{
ch = digraph(erased, ch);
erased = 0;
}
#endif
/* inhibit detection of special chars (except ^J) after a ^V */
if (quoted && ch != '\n')
{
ch |= 256;
}
/* process the character */
switch(ch)
{
case ctrl('V'):
qaddch('^');
qaddch('\b');
quoted = TRUE;
break;
case ctrl('['):
return -1;
case '\n':
#if OSK
case '\l':
#else
case '\r':
#endif
clrtoeol();
goto BreakBreak;
case '\b':
if (len > 0)
{
len--;
#ifndef NO_DIGRAPH
erased = buf[len];
#endif
for (ch = widths[len]; ch > 0; ch--)
addch('\b');
if (mode == MODE_EX)
{
clrtoeol();
}
tab -= widths[len];
}
else
{
return -1;
}
break;
default:
/* strip off quotation bit */
if (ch & 256)
{
ch &= ~256;
quoted = FALSE;
qaddch(' ');
qaddch('\b');
}
/* add & echo the char */
if (len < bsize - 1)
{
if (ch == '\t')
{
widths[len] = *o_tabstop - (tab % *o_tabstop);
addstr(" " + 8 - widths[len]);
tab += widths[len];
}
else if (ch > 0 && ch < ' ') /* > 0 by GB */
{
addch('^');
addch(ch + '@');
widths[len] = 2;
tab += 2;
}
else if (ch == '\177')
{
addch('^');
addch('?');
widths[len] = 2;
tab += 2;
}
else
{
addch(ch);
widths[len] = 1;
tab++;
}
buf[len++] = ch;
}
else
{
beep();
}
}
}
BreakBreak:
refresh();
buf[len] = '\0';
return len;
}
/* ring the terminal's bell */
void beep()
{
if (*o_vbell)
{
do_VB();
refresh();
}
else if (*o_errorbells)
{
ttywrite("\007", 1);
}
}
static int manymsgs; /* This variable keeps msgs from overwriting each other */
static char pmsg[80]; /* previous message (waiting to be displayed) */
static int showmsg()
{
/* if there is no message to show, then don't */
if (!manymsgs)
return FALSE;
/* display the message */
move(LINES - 1, 0);
if (*pmsg)
{
standout();
qaddch(' ');
qaddstr(pmsg);
qaddch(' ');
standend();
}
clrtoeol();
manymsgs = FALSE;
return TRUE;
}
void endmsgs()
{
if (manymsgs)
{
showmsg();
addch('\n');
}
}
/* Write a message in an appropriate way. This should really be a varargs
* function, but there is no such thing as vwprintw. Hack!!!
*
* In MODE_EX or MODE_COLON, the message is written immediately, with a
* newline at the end.
*
* In MODE_VI, the message is stored in a character buffer. It is not
* displayed until getkey() is called. msg() will call getkey() itself,
* if necessary, to prevent messages from being lost.
*
* msg("") - clears the message line
* msg("%s %d", ...) - does a printf onto the message line
*/
/*VARARGS1*/
void msg(fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7)
char *fmt;
long arg1, arg2, arg3, arg4, arg5, arg6, arg7;
{
if (mode != MODE_VI)
{
sprintf(pmsg, fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
qaddstr(pmsg);
addch('\n');
exrefresh();
}
else
{
/* wait for keypress between consecutive msgs */
if (manymsgs)
{
getkey(WHEN_MSG);
}
/* real message */
sprintf(pmsg, fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
manymsgs = TRUE;
}
}
/* This function calls refresh() if the option exrefresh is set */
void exrefresh()
{
char *scan;
/* If this ex command wrote ANYTHING set exwrote so vi's : command
* can tell that it must wait for a user keystroke before redrawing.
*/
for (scan=kbuf; scan<stdscr; scan++)
if (*scan == '\n')
exwrote = TRUE;
#if MICROSOFT /* avoid compiler bug */
scan = stdscr;
#define stdscr scan
#endif
/* now we do the refresh thing */
if (*o_exrefresh)
{
refresh();
}
else
{
wqrefresh(stdscr);
}
#if MICROSOFT
#undef stdscr
stdscr = scan;
#endif
if (mode != MODE_VI)
{
manymsgs = FALSE;
}
}
/* These are used for typeahead, and also for fudging the visual @ command */
static char keybuf[100]; /* array of already-read keys */
static int nkeys; /* total number of keys in keybuf */
static int next; /* index of next key to return */
#ifndef NO_AT
static int atnext; /* index of next key for "@", or 0 normally */
int fromcutbuf(cbname)
int cbname;
{
int len;
/* fail if we're already doing an @ macro */
if (atnext > 0 && keybuf[atnext])
{
msg("Can't nest @ commands");
return FALSE;
}
/* use the empty portion of keybuf[] to get chars from the cut buffer */
len = cb2str(cbname, keybuf + nkeys, sizeof keybuf - nkeys);
if (len < 0)
{
msg("Invalid cut buffer name. Must be a-z");
return FALSE;
}
if (len == 0)
{
msg("Cut buffer \"%c is empty", cbname);
return FALSE;
}
else if (len >= sizeof keybuf - nkeys)
{
msg("Cut buffer \"%c is too large to execute", cbname);
return FALSE;
}
/* prepare to "read" those keys on subsequent getkey() calls */
atnext = nkeys;
return TRUE;
}
#endif
/* This array describes mapped key sequences */
static struct _keymap
{
char *name; /* name of the key, or NULL */
char rawin[LONGKEY]; /* the unmapped version of input */
char cooked[80]; /* the mapped version of input */
int len; /* length of the unmapped version */
int when; /* when is this key mapped? */
}
mapped[MAXMAPS];
#if !MSDOS && !TOS
# if BSD || COHERENT
static jmp_buf env_timeout;
static int dummy()
{
longjmp(env_timeout, 1);
return 0;
}
# else
static int dummy()
{
}
# endif
#endif
/* This function reads in a keystroke for VI mode. It automatically handles
* key mapping.
*/
int getkey(when)
int when; /* which bits must be ON? */
{
static char *cooked; /* rawin, or pointer to converted key */
static int oldwhen; /* "when" from last time */
static int oldleft;
static long oldtop;
static long oldnlines;
static char *cshape; /* current cursor shape */
REG char *kptr; /* &keybuf[next] */
REG struct _keymap *km; /* used to count through keymap */
REG int i, j, k;
#ifdef DEBUG
watch();
#endif
/* if this key is needed for delay between multiple error messages,
* then reset the manymsgs flag and abort any mapped key sequence.
*/
if (showmsg())
{
if (when == WHEN_MSG)
{
qaddstr("[More...]");
refresh();
cooked = (char *)0;
}
else if (when == WHEN_VIINP || when == WHEN_VIREP)
{
redraw(cursor, TRUE);
}
}
#ifndef NO_AT
/* if we're in the middle of a visual @ macro, take atnext */
if (atnext > 0)
{
if (keybuf[atnext])
{
return keybuf[atnext++];
}
atnext = 0;
}
#endif
/* if we're doing a mapped key, get the next char */
if (cooked && *cooked)
{
return *cooked++;
}
/* if keybuf is empty, fill it */
if (next == nkeys)
{
#ifndef NO_CURSORSHAPE
/* make sure the cursor is the right shape */
if (has_CQ)
{
cooked = cshape;
switch (when)
{
case WHEN_EX: cooked = CX; break;
case WHEN_VICMD: cooked = CV; break;
case WHEN_VIINP: cooked = CI; break;
case WHEN_VIREP: cooked = CR; break;
}
if (cooked != cshape)
{
cshape = cooked;
switch (when)
{
case WHEN_EX: do_CX(); break;
case WHEN_VICMD: do_CV(); break;
case WHEN_VIINP: do_CI(); break;
case WHEN_VIREP: do_CR(); break;
}
}
cooked = (char *)0;
}
#endif
#ifndef NO_SHOWMODE
/* if "showmode" then say which mode we're in */
if (*o_smd
&& mode == MODE_VI
&& (when != oldwhen || topline != oldtop || leftcol != oldleft || nlines != oldnlines))
{
oldwhen = when;
oldtop = topline;
oldleft = leftcol;
oldnlines = nlines;
if (when & WHEN_VICMD)
{
redraw(cursor, FALSE);
move(LINES - 1, COLS - 10);
standout();
addstr("Command");
standend();
redraw(cursor, FALSE);
}
else if (when & WHEN_VIINP)
{
redraw(cursor, TRUE);
move(LINES - 1, COLS - 10);
standout();
addstr(" Input ");
standend();
redraw(cursor, TRUE);
}
else if (when & WHEN_VIREP)
{
redraw(cursor, TRUE);
move(LINES - 1, COLS - 10);
standout();
addstr("Replace");
standend();
redraw(cursor, TRUE);
}
}
else
#endif
/* redraw if getting a VI command */
if (when & WHEN_VICMD)
{
redraw(cursor, FALSE);
}
/* read the rawin keystrokes */
refresh();
while ((nkeys = ttyread(keybuf, sizeof keybuf)) < 0)
{
/* terminal was probably resized */
*o_lines = LINES;
*o_columns = COLS;
if (when & (WHEN_VICMD|WHEN_VIINP|WHEN_VIREP))
{
redraw(MARK_UNSET, FALSE);
redraw(cursor, (when & WHEN_VICMD) == 0);
refresh();
}
}
next = 0;
/* if nkeys == 0 then we've reached EOF of an ex script. */
if (nkeys == 0)
{
tmpabort(TRUE);
move(LINES - 1, 0);
clrtoeol();
refresh();
endwin();
exit(1);
}
}
/* see how many mapped keys this might be */
kptr = &keybuf[next];
for (i = j = 0, k = -1, km = mapped; i < MAXMAPS; i++, km++)
{
if ((km->when & when) && km->len > 0 && *km->rawin == *kptr)
{
if (km->len > nkeys - next)
{
if (!strncmp(km->rawin, kptr, nkeys - next))
{
j++;
}
}
else
{
if (!strncmp(km->rawin, kptr, km->len))
{
j++;
k = i;
}
}
}
}
/* if more than one, try to read some more */
while (j > 1)
{
#if BSD || COHERENT
if (setjmp(env_timeout))
{
/* we timed out - assume no mapping */
j = 0;
break;
}
#endif
#if ANY_UNIX || DF_POSIX
signal(SIGALRM, dummy);
#endif
#if OSK
signal(SIGQUIT, dummy);
#endif
alarm((unsigned)*o_keytime);
i = nkeys;
if ((k = ttyread(keybuf + nkeys, sizeof keybuf - nkeys)) >= 0)
{
nkeys += k;
}
alarm(0);
#if OSK
# ifndef DEBUG
signal(SIGQUIT, SIG_IGN);
# endif
#endif
/* if we couldn't read any more, pretend 0 mapped keys */
if (i == nkeys)
{
j = 0;
}
else /* else we got some more - try again */
{
for (i = j = 0, k = -1, km = mapped; i < MAXMAPS; i++, km++)
{
if ((km->when & when) && km->len > 0 && *km->rawin == *kptr)
{
if (km->len > nkeys - next)
{
if (!strncmp(km->rawin, kptr, nkeys - next))
{
j++;
}
}
else
{
if (!strncmp(km->rawin, kptr, km->len))
{
j++;
k = i;
}
}
}
}
}
}
/* if unambiguously mapped key, use it! */
if (j == 1 && k >= 0)
{
next += mapped[k].len;
cooked = mapped[k].cooked;
#ifndef NO_EXTENSIONS
if ((when & (WHEN_VIINP|WHEN_VIREP))
&& (mapped[k].when & WHEN_INMV))
{
return 0; /* special case, means "a movement char follows" */
}
else
#endif
{
return *cooked++;
}
}
else
/* assume key is unmapped, but still translate weird erase key to '\b' */
if (keybuf[next] == ERASEKEY && when != 0)
{
next++;
return '\b';
}
else if (keybuf[next] == '\0')
{
next++;
return ('A' & 0x1f);
}
else
{
return keybuf[next++];
}
}
/* This function maps or unmaps a key */
void mapkey(rawin, cooked, when, name)
char *rawin; /* the input key sequence, before mapping */
char *cooked;/* after mapping */
short when; /* bitmap of when mapping should happen */
char *name; /* name of the key, if any */
{
int i, j;
#ifndef NO_EXTENSIONS
/* if the mapped version starts with the word "visual" then set WHEN_INMV */
if (!strncmp(cooked, "visual ", 7))
{
when |= WHEN_INMV;
cooked += 7;
}
/* if WHEN_INMV is set, then WHEN_VIINP and WHEN_VIREP must be set */
if (when & WHEN_INMV)
{
when |= (WHEN_VIINP | WHEN_VIREP);
}
#endif
/* see if the key sequence was mapped before */
j = strlen(rawin);
for (i = 0; i < MAXMAPS; i++)
{
if (mapped[i].len == j
&& !strncmp(mapped[i].rawin, rawin, j)
&& (mapped[i].when & when))
{
break;
}
}
/* if not already mapped, then try to find a new slot to use */
if (i == MAXMAPS)
{
for (i = 0; i < MAXMAPS && mapped[i].len > 0; i++)
{
}
}
/* no room for the new key? */
if (i == MAXMAPS)
{
msg("No room left in the key map table");
return;
}
/* map the key */
if (cooked && *cooked)
{
/* Map the key */
mapped[i].len = j;
strncpy(mapped[i].rawin, rawin, j);
strcpy(mapped[i].cooked, cooked);
mapped[i].when = when;
mapped[i].name = name;
}
else /* unmap the key */
{
mapped[i].len = 0;
}
}
/* Dump keys of a given type - WHEN_VICMD dumps the ":map" keys, and
* WHEN_VIINP|WHEN_VIREP dumps the ":map!" keys
*/
void dumpkey(when)
int when; /* WHEN_XXXX of mappings to be dumped */
{
int i, len, mlen;
char *scan;
char *mraw;
for (i = 0; i < MAXMAPS; i++)
{
/* skip unused entries, or entries that don't match "when" */
if (mapped[i].len <= 0 || !(mapped[i].when & when))
{
continue;
}
/* dump the key label, if any */
len = 8;
if (mapped[i].name)
{
qaddstr(mapped[i].name);
len -= strlen(mapped[i].name);
}
do
{
qaddch(' ');
} while (len-- > 0);
/* dump the raw version */
len = 0;
mlen = mapped[i].len;
mraw = mapped[i].rawin;
for (scan = mraw; scan < mraw + mlen; scan++)
{
if (UCHAR(*scan) < ' ' || *scan == '\177')
{
qaddch('^');
qaddch(*scan ^ '@');
len += 2;
}
else
{
qaddch(*scan);
len++;
}
}
do
{
qaddch(' ');
} while (++len < 8);
/* dump the mapped version */
#ifndef NO_EXTENSIONS
if ((mapped[i].when & WHEN_INMV) && (when & (WHEN_VIINP|WHEN_VIREP)))
{
qaddstr("visual ");
}
#endif
for (scan = mapped[i].cooked; *scan; scan++)
{
if (UCHAR(*scan) < ' ' || *scan == '\177')
{
qaddch('^');
qaddch(*scan ^ '@');
}
else
{
qaddch(*scan);
}
}
addch('\n');
exrefresh();
}
}
#ifndef MKEXRC
/* This function saves the current configuration of mapped keys to a file */
void savekeys(fd)
int fd; /* file descriptor to save them to */
{
int i;
char buf[80];
/* now write a map command for each key other than the arrows */
for (i = 0; i < MAXMAPS; i++)
{
/* ignore keys that came from termcap */
if (mapped[i].name)
{
continue;
}
/* If this isn't used, ignore it */
if (mapped[i].len <= 0)
{
continue;
}
/* write the map command */
#ifndef NO_EXTENSIONS
if (mapped[i].when & WHEN_INMV)
{
#if OSK
char fmt[80];
sprintf(fmt, "map%%s %%.%ds %%s\n", mapped[i].len);
sprintf(buf, fmt,
(mapped[i].when & WHEN_VICMD) ? "" : "!",
#else
sprintf(buf, "map%s %.*s visual %s\n",
(mapped[i].when & WHEN_VICMD) ? "" : "!",
mapped[i].len,
#endif
mapped[i].rawin,
mapped[i].cooked);
twrite(fd, buf, strlen(buf));
}
else
#endif
{
if (mapped[i].when & WHEN_VICMD)
{
#if OSK
char fmt[80];
sprintf(fmt, "map %%.%ds %%s\n", mapped[i].len);
sprintf(buf, fmt,
#else
sprintf(buf, "map %.*s %s\n", mapped[i].len,
#endif
mapped[i].rawin,
mapped[i].cooked);
twrite(fd, buf, strlen(buf));
}
if (mapped[i].when & (WHEN_VIINP | WHEN_VIREP))
{
#if OSK
char fmt[80];
sprintf(fmt, "map! %%.%ds %%s\n", mapped[i].len);
sprintf(buf, fmt,
#else
sprintf(buf, "map! %.*s %s\n", mapped[i].len,
#endif
mapped[i].rawin,
mapped[i].cooked);
twrite(fd, buf, strlen(buf));
}
}
}
}
#endif